package com.hyzx.qbasic.admin.domain.service.impl;

import com.github.pagehelper.PageInfo;
import com.hyzx.qbasic.admin.model.dto.ProcessStepInfo;
import com.hyzx.qbasic.admin.model.dto.ProcessTaskNode;
import com.hyzx.qbasic.admin.model.po.AdminUserInfoPO;
import com.hyzx.qbasic.admin.model.po.ProcessDefinitionPO;
import com.hyzx.qbasic.admin.model.po.ProcessDefinitionPagedInfoPO;
import com.hyzx.qbasic.admin.model.po.ProcessTaskDefinitionPO;
import com.hyzx.qbasic.admin.model.vo.req.admin.AddProcessDefinitionReq;
import com.hyzx.qbasic.admin.model.vo.req.admin.DeleteProcessDefinitionReq;
import com.hyzx.qbasic.admin.model.vo.req.admin.EditProcessDefinitionReq;
import com.hyzx.qbasic.admin.model.vo.req.admin.GetProcessDefinitionListReq;
import com.hyzx.qbasic.admin.model.vo.res.BaseOperationRes;
import com.hyzx.qbasic.admin.model.vo.res.admin.GetProcessDefinitionInfoRes;
import com.hyzx.qbasic.admin.model.vo.res.admin.GetProcessStepsRes;
import com.hyzx.qbasic.admin.common.util.OperationResUtils;
import com.hyzx.qbasic.admin.domain.repository.IProcessDefinitionRepository;
import com.hyzx.qbasic.admin.domain.service.IAdminUserService;
import com.hyzx.qbasic.admin.domain.service.IProcessDefinitionService;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Created by 黄江华 on 2018/11/22
 */

@Service
public class ProcessDefinitionServiceImpl implements IProcessDefinitionService {
    @Autowired
    private IProcessDefinitionRepository definitionRepository;

    @Autowired
    private IAdminUserService userService;

    @Override
    public PageInfo<ProcessDefinitionPagedInfoPO> getPagedProcessDefinitionList(GetProcessDefinitionListReq req) {
        PageInfo<ProcessDefinitionPagedInfoPO> definitionList = definitionRepository.getPagedProcessDefinitionList(
                req.getPageNum(), req.getPageSize(), req.getProcessCategoryId(), req.getProcessDefinitionName());

        return definitionList;
    }

    @Override
    public List<ProcessDefinitionPagedInfoPO> getProcessDefinitionListByCategoryId(Integer processCategoryId) {
        return definitionRepository.getProcessDefinitionListByCategoryId(processCategoryId);
    }

    @Override
    public List<ProcessDefinitionPagedInfoPO> getProcessDefinitionListByCategoryKey(String processCategoryKey) {
        return definitionRepository.getProcessDefinitionListByCategoryKey(processCategoryKey);
    }

    @Override
    public BaseOperationRes addProcessDefinition(AddProcessDefinitionReq req) {
        ProcessDefinitionPO definitionPO = new ProcessDefinitionPO();
        definitionPO.setProcessDefinitionName(req.getProcessDefinitionName());
        definitionPO.setProcessCategoryId(req.getProcessCategoryId());
        definitionPO.setProcessNodes(req.getProcessNodes());
        definitionPO.setCreateTime(new Date());
        definitionPO.setUpdateTime(new Date());
        definitionPO.setDeleted((byte) 0);

        boolean success = definitionRepository.addProcessDefinition(definitionPO);
        if (!success)
            return OperationResUtils.fail("新增流程失败");

        //保存流程任务节点
        if (CollectionUtils.isNotEmpty(req.getTaskNodes())) {
            saveTaskDefinition(req.getTaskNodes(), definitionPO.getProcessDefinitionId());
        }

        return OperationResUtils.success();
    }

    @Override
    public BaseOperationRes deleteProcessDefinition(DeleteProcessDefinitionReq req) {
        boolean success = definitionRepository.deleteProcessDefinition(req.getProcessDefinitionIdList());
        if (!success)
            return OperationResUtils.fail("删除流程失败");

        return OperationResUtils.success();
    }

    @Override
    public GetProcessDefinitionInfoRes getProcessDefinitionInfo(int processDefinitionId) {
        GetProcessDefinitionInfoRes res = new GetProcessDefinitionInfoRes();
        List<ProcessTaskNode> taskNodes = Collections.EMPTY_LIST;
        List<AdminUserInfoPO> userList = Collections.EMPTY_LIST;

        ProcessDefinitionPO processDefinition = definitionRepository.getProcessDefinition(processDefinitionId);
        if (processDefinition == null) {
            res.setProcessDefinition(null);
            res.setTaskNodes(taskNodes);
            res.setUserList(userList);

            return res;
        }

        byte processNodes = processDefinition.getProcessNodes();
        if (processNodes != 0) {
            taskNodes = new ArrayList<>(processNodes);
            List<ProcessTaskDefinitionPO> taskDefinitions = definitionRepository.getTaskDefinition(processDefinitionId);

            for (int i = 1; i <= processNodes; i++) {
                final int index = i;
                List<ProcessTaskDefinitionPO> filterTaskDefinitions = taskDefinitions.stream()
                        .filter(t -> t.getTaskNode().byteValue() == index)
                        .collect(Collectors.toList());

                if (CollectionUtils.isNotEmpty(filterTaskDefinitions)) {
                    ProcessTaskNode taskNode = new ProcessTaskNode();
                    taskNode.setTaskName(filterTaskDefinitions.get(0).getTaskName());
                    taskNode.setCandidate(filterTaskDefinitions.stream()
                            .map(t -> Integer.valueOf(t.getCandidate()))
                            .collect(Collectors.toList()));

                    taskNodes.add(taskNode);
                }
            }

            List<Integer> candidateList = taskDefinitions.stream()
                    .map(t -> Integer.valueOf(t.getCandidate()))
                    .collect(Collectors.toList());
            userList = userService.getMultiUserInfo(candidateList);
        }

        res.setProcessDefinition(processDefinition);
        res.setTaskNodes(taskNodes);
        res.setUserList(userList);

        return res;
    }

    @Override
    public BaseOperationRes editProcessDefinition(EditProcessDefinitionReq req) {
        //检查流程分类是否存在
        ProcessDefinitionPO existedDefinition = definitionRepository.getProcessDefinition(req.getProcessDefinitionId());
        if (existedDefinition == null)
            return OperationResUtils.fail("流程不存在");

        ProcessDefinitionPO definitionPO = new ProcessDefinitionPO();
        definitionPO.setProcessDefinitionId(req.getProcessDefinitionId());
        definitionPO.setProcessDefinitionName(req.getProcessDefinitionName());
        definitionPO.setProcessCategoryId(req.getProcessCategoryId());
        definitionPO.setProcessNodes(req.getProcessNodes());
        definitionPO.setUpdateTime(new Date());

        boolean success = definitionRepository.editProcessDefinition(definitionPO);
        if (!success)
            return OperationResUtils.fail("编辑流程失败");

        //保存流程任务节点
        definitionRepository.deleteTaskDefinition(definitionPO.getProcessDefinitionId());
        if (CollectionUtils.isNotEmpty(req.getTaskNodes())) {
            saveTaskDefinition(req.getTaskNodes(), definitionPO.getProcessDefinitionId());
        }

        return OperationResUtils.success();
    }

    @Override
    public GetProcessStepsRes getProcessSteps(Integer processDefinitionId) {
        GetProcessStepsRes res = new GetProcessStepsRes();
        res.setActive(0);

        List<ProcessTaskDefinitionPO> taskDefinitions = definitionRepository.getTaskDefinition(processDefinitionId);
        if (CollectionUtils.isEmpty(taskDefinitions)) {
            res.setSteps(Collections.emptyList());
            return res;
        }

        List<Integer> candidateIdList = taskDefinitions.stream()
                .map(t -> Integer.valueOf(t.getCandidate()))
                .collect(Collectors.toList());
        List<AdminUserInfoPO> candidateList = userService.getMultiUserInfo(candidateIdList);

        int maxTaskNode = taskDefinitions.get(taskDefinitions.size() - 1).getTaskNode();
        List<ProcessStepInfo> steps = new ArrayList<>(maxTaskNode);

        for (int i = 1; i <= maxTaskNode; i++) {
            final int index = i;
            List<ProcessTaskDefinitionPO> filterTaskDefinitions = taskDefinitions.stream()
                    .filter(t -> t.getTaskNode().byteValue() == index)
                    .collect(Collectors.toList());

            //获取流程节点候选人列表
            String candidates = filterTaskDefinitions.stream()
                    .map(task -> candidateList.stream()
                            .filter(u -> u.getUserId().equals(Integer.parseInt(task.getCandidate())))
                            .findFirst()
                            .orElseGet(() -> new AdminUserInfoPO())
                    )
                    .map(user -> user.getRealName())
                    .reduce((user1, user2) -> user1 + " " + user2).orElse("");

            ProcessStepInfo stepInfo = new ProcessStepInfo();
            stepInfo.setTitle(filterTaskDefinitions.get(0).getTaskName());
            stepInfo.setCandidate(candidates);

            steps.add(stepInfo);
        }

        ProcessStepInfo lastStep = new ProcessStepInfo();
        lastStep.setTitle("完成");
        steps.add(lastStep);

        res.setSteps(steps);
        return res;
    }

    private void saveTaskDefinition(List<ProcessTaskNode> taskNodes, Integer processDefinitionId) {
        List<ProcessTaskDefinitionPO> tasks = new ArrayList<>();

        for (int i = 0; i < taskNodes.size(); i++) {
            ProcessTaskNode taskNode = taskNodes.get(i);

            for (Integer candidate : taskNode.getCandidate()) {
                ProcessTaskDefinitionPO taskDefinition = new ProcessTaskDefinitionPO();
                taskDefinition.setProcessDefinitionId(processDefinitionId);
                taskDefinition.setTaskName(taskNode.getTaskName());
                taskDefinition.setCandidate(candidate.toString());
                taskDefinition.setTaskNode((byte) (i + 1));
                taskDefinition.setCreateTime(new Date());

                tasks.add(taskDefinition);
            }
        }

        definitionRepository.addProcessDefinitionTask(tasks);
    }
}
